"""
接口路由 | 业务层
"""

from fastapi import FastAPI
from fastapi.routing import APIRoute
from pydantic.v1.fields import ModelField

from biz.base_biz import (
    CreatorBase,
    DeleterBase,
    GetterBase,
    UpdatorBase,
    ValidatorBase,
)
from components.logger import log
from mapping import APIRouteMapping
from models import (
    PageSorting,
    POBase,
)
from models.po import APIRoutePO
from models.request import APIRouteListRequest
from my_typing import *
from service import APIRouteService


class Model:
    """ 模型信息 """

    ID_FIELD: str = "route_id"
    NAME: str = "接口路由"
    PO = APIRoutePO
    SERVICE = APIRouteService


class APIRouteValidator(ValidatorBase):
    """ 接口路由 | 校验器 """

    model_id_field = Model.ID_FIELD
    model_name = Model.NAME
    model_po = Model.PO
    model_service = Model.SERVICE


class APIRouteGetter(GetterBase):
    """ 接口路由 | 获取器 """

    model_mapping = APIRouteMapping
    model_validator = APIRouteValidator

    @staticmethod
    def get_list_sorting(request: APIRouteListRequest) -> DictStrInt:
        return PageSorting.get_sorting_by_map(
            {
                "dependencies": request.sort_by_dependency,
                "methods": request.sort_by_method,
                "path": request.sort_by_path,
                "summary": request.sort_by_summary,
                "tags": request.sort_by_tag,
                "update_time": request.sort_by_update_time,
            }
        )

    @classmethod
    def get_list_query(cls, request: APIRouteListRequest) -> dict:
        condition = cls.Condition()

        condition.add_eq("dependencies", value=request.dependency)
        condition.add_eq("deprecated", value=request.deprecated)
        condition.add_eq("methods", value=request.method)
        condition.add_regex("name", value=request.name)
        condition.add_regex("path", value=request.path)
        condition.add_regex("summary", value=request.summary)
        condition.add_eq("tag", value=request.tag)

        return condition.get_query()

    @staticmethod
    def get_calc_all(app: FastAPI) -> Iterator[APIRoutePO]:
        """ 获取所有接口路由（From 当前计算） """

        def _get_route_dependencies(route_: APIRoute) -> ListStr:
            """ 获取路由的依赖项 """

            return [
                _.dependency.__name__
                for _ in route_.dependencies
            ]

        def _get_request_model(route_: APIRoute) -> StrOrNone:
            """ 获取请求的模型 """

            body_field: ModelField | None = route_.body_field

            if body_field is None:
                return None

            return str(body_field.type_)

        api_route_list: Iterator[APIRoute] = (
            _route
            for _route in app.routes
            if isinstance(_route, APIRoute)
        )

        for route in api_route_list:
            yield APIRoutePO(
                **{
                    **route.__dict__,
                    "dependencies": _get_route_dependencies(route),
                    "deprecated": bool(route.deprecated),
                    "path_regex": str(route.path_regex),
                    "request_model": _get_request_model(route),
                    "response_model": str(route.response_model),
                },
            )

    @staticmethod
    def get_db_all() -> Iterator[APIRoutePO]:
        """ 获取所有接口路由（From DB） """

        yield from APIRouteService.find()


class APIRouteSetter:
    """ 接口路由 | 设置器 """

    @staticmethod
    def update_api_route(app: FastAPI) -> None:
        """
        更新接口路由

        1. 获取所有接口路由（From DB）
        2. 获取所有接口路由（From 当前计算）
        3. 对比差异，更新数据库
            3.1 DB 存在，当前不存在，删除
            3.2 DB 不存在，当前存在，新增
            3.3 DB 存在，当前存在（且数据发生变化），更新
        """

        db_all: Iterator[APIRoutePO] = APIRouteGetter.get_db_all()
        calc_all: Iterator[APIRoutePO] = APIRouteGetter.get_calc_all(app=app)

        to_create_po_list: list[APIRoutePO] = []
        to_delete_id_list: list[APIRoutePO] = []
        to_update_po_list: list[APIRoutePO] = []

        db_unique_id_2_po: dict[str, APIRoutePO] = {
            po.unique_id: po
            for po in db_all
        }
        calc_unique_id_2_po: dict[str, APIRoutePO] = {
            po.unique_id: po
            for po in calc_all
        }

        for unique_id, calc_po in calc_unique_id_2_po.items():
            # 当前数据在 DB 中存在（且数据发生变化）：更新
            if db_po := db_unique_id_2_po.get(unique_id):
                db_dict = db_po.model_dump(exclude=POBase.base_fields())
                calc_dict = calc_po.model_dump(exclude=POBase.base_fields())

                # 数据无变化，跳过
                if db_dict == calc_dict:
                    continue

                calc_po.id = db_po.id
                calc_po.create_time = db_po.create_time
                to_update_po_list.append(calc_po)

                UpdatorBase.version_log(db_po, calc_po)

            # 当前数据在 DB 中不存在：新增
            else:
                to_create_po_list.append(calc_po)

                CreatorBase.version_log(calc_po)

        for unique_id, db_po in db_unique_id_2_po.items():
            if unique_id not in calc_unique_id_2_po:
                # DB 数据在当前数据中不存在：删除
                to_delete_id_list.append(db_po)

                DeleterBase.version_log(db_po)

        if to_create_po_list:
            APIRouteService.insert_many(to_create_po_list)

        if to_update_po_list:
            APIRouteService.update_many(to_update_po_list)

        if to_delete_id_list:
            APIRouteService.delete_many(to_delete_id_list)

        if (
            to_create_po_list
            or to_update_po_list
            or to_delete_id_list
        ):
            log.success(
                f"[接口路由][更新接口路由]"
                f"[DB: {len(db_unique_id_2_po)}]"
                f"[CALC: {len(calc_unique_id_2_po)}]"
                f"[CREATE: {len(to_create_po_list)}]"
                f"[UPDATE: {len(to_update_po_list)}]"
                f"[DELETE: {len(to_delete_id_list)}]"
            )
